Rust memo
RustはRAII、つまり所有権を用いた外部リソースの管理に対応
unsafe { File::from_raw_fd(3) };
ヒープ領域の解放だけでなく、File など外部リソースに対応する値に関してもユーザー定義のデストラクタ (Drop::drop) が即座に呼ばれる
弱参照 Weakは他の強参照に帰省する形でのみ参照先を保持でき、実際に参照するときは強参照への昇格を試みる
強参照がなくなってもヒープ領域自体は残るが中身はdrop
mutableなobjectに対して、immutableな借用をした場合、借用中objectへの書き込みはできない。読み取りは可能。
code:1.rs
let mut a = 100;
{
let x = &a;
a = 200; // write NG
// => error: cannot assign to x because it is borrowed
a; // read OK
}
a = 200; // write OK
借用は全てのownerのスコープよりも長く生きれない
immutableなobjectはimmutableな借用のみ可能
mutableなobjectはimmutable/mutableいずれの借用も可能
immutableな借用は、元のobjectのimmutable/mutableに関係なく、read only
mutableな借用中は、ownerによる使用はできない。(Readも所有権移動も)
immutableな借用は又貸し可能。mutableは無理。
mutable借用のimmutable/mutableな又貸しは可能。
https://gyazo.com/1db08940df53b67cd1fd6b88fb969b98
xがowner、yがborrowerであり、borrowerであるyが先に宣言されている分、yがxより長生きすることになりエラー。
code:1.rs
let y: &i32;
let x = 5;
y = &x;
println!("{}", y);
Mutable borrow automatically changes to immutable?
re-borrowingによる借用のmutable->immutableの変換
Box<> vs 生ポインタ
rustのメモリ安全性内でのポインタ操作・リソース管理
トレイトオブジェクトとしての利用
特定のトレイトを実装している型であることを定める
コンパイル時にサイズが分からない型の利用
Deref trait
カスタマイズしたポインタ型の定義
structインスタンスのポインタ型がstruct内要素のポインタ型を取得するようにカスタマイズしている。
code:1.rs
use std::ops::Deref;
struct DerefExample<T> {
value: T,
}
impl<T> Deref for DerefExample<T> {
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
fn main() {
let x = DerefExample {value: 'a'};
assert_eq!('a', *x);
}
Derefによる型強制
StringはDeref<Target=str>を実装しているので&Stringが&strに型強制される。
code:1.rs
fn foo(s: &str) {
}
let owned = "Hello".to_string();
foo(&owned);
Borrow
code:1.rs
fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where k: Borrow<Q>, Q: Hash + Eq
AsRefとBorrowは、&selfを&Tに変換し得るもの。
AsRefはgenericなobjectをreferenceに変換する
BorrowはHashMapのキーなどハッシュ値や比較結果を気にする場面で
ToOwnedは、&seltをTに変換し得るもの。(cloneはoutputがSelfのみ)
Into/From
https://gyazo.com/20095ecfa080a01e6533bcf5a74be109
new type pattern
orphan ruleにより外部トレイトを外部の型に実装することはできない。
トレイトか型がローカルであればOK
wrapperすればOK
Deriving external traits on external structs
Wrapper typeにDeref traitを実装して内部の型が持つ全てのメソッドを新しい型に持たせる
静的に値を混同しないことを強制する。
Millimeters(u32), Meters(u32)
型実装の詳細を抽象化
非公開の内部の型のAPIとは異なる公開APIを新しい型が提供できる。
内部実装の隠匿
People(HashMap<i32, String>)で公開APIの追加メソッドだけと相互作用することになる
fold
code:1.rs
let sum = (1..4).fold(0, |sum, x| sum + x); -> 6
PhantomData<T>
構造体の型引数を強制的に消費するときに用いる。
code:1.rs
use std::maker::PhantomData;
trait Unit {}
struct UnitFloat<U: Unit> {
inner: f64,
_maker: PhantomData<fn() -> U>,
}
PhantomData<fn() -> U>とPhantomData<U>の動作は微妙に異なる。
PhantomData<U>はUを所有していると見なされる。
関連型
1つのトレイトで複数の型をグループ化することで不要な型宣言をなくす。
code:1.rs
trait Graph<N, E> {
fn has_edge(&self, &N, &N) -> bool;
fn edges(&self, &N) -> Vec<E>;
}
のトレイト境界を与えるためにNとEのジェネリックも宣言する必要が出てしまう。
code:1.rs
fn distance<N, E, G: Graph<N, E>>(graph: &G, start; &N, end: &N) -> u32 {..}
その代わり以下のよう関連型を定義すると、
code:1.rs
trait Graph {
type N;
type E;
fn has_edge(&self, &Self::N, &Self::N) -> bool;
fn edges(&self, &Self::N) -> Vec(Self::E>;
}
以下でOK
code:1.rs
fn distance<G>(graph: &G, start: &G::N, end: &G::N) -> u32 {..}
rustをnostdで使う
From<T> and Into<U>はfailできない型変換でoriginalの値を消費する
Fromを実装するとIntoは自動実装
code:1.rs
let converted_value = From::from(original_value);
let converted_value = TargetType::from(original_value);
let converted_value = original_value.into();
let converted_value = Into::into(original_value);
特定の型だけでなくconvertされる型を利用する
code:1.rs
fn do_something<U: Into<TargetType<T>>>(value: U) {
let converted_value = value.into();
}
code: 1.rs
struct SortedVec<T>(Vec<T>);
impl<'a, T: Ord + Clone> From<&'a T> for SortedVec<T> { fn from(slice: &T) -> Self { let mut vec = slice.to_owned();
vec.sort();
SortedVec(vec)
}
}
impl<T: Ord + Clone> From<Vec<T>> for SortedVec<T> {
fn from(mut vec: Vec<T>) -> Self {
vec.sort();
SortedVec(vec)
}
}
oはRawOrigin<AccountId>に型変換可能。(Optionをつけることでfailも可能)
code:1.rs
/// Ensure that the origin o represents a signed extrinsic (i.e. transaction).
/// Returns Ok with the account that signed the extrinsic or an Err otherwise.
pub fn ensure_signed<OuterOrigin, AccountId>(o: OuterOrigin) -> Result<AccountId, &'static str>
where OuterOrigin: Into<Option<RawOrigin<AccountId>>>
{
match o.into() {
Some(RawOrigin::Signed(t)) => Ok(t),
_ => Err("bad origin: expected to be a signed origin"),
}
}
wasm-wallet
mio
OSよって異なるI/O多重化のためのシステムコールを薄く抽象化するためのライブラリ
I/O多重化:複数の接続をノンブロッキングに処理する。
ChaChaRng instead of OsRng in the no_std and wasm environment
stdで実装されているcollections(dynamic data structures, like vec, string, hashmap)はheap(global dynamic memory allocator)を使っているが、core::allocはアロケータAPIは定義されていないので自力で実装する必要がある。
staticなアロケータを定義しGlobalAlloctraitを実装する。alloc()とdealloc()。
また、Out Of Memory (OOM)エラーのハンドリングも定義。
&mut &mutをつけるとなぜか以下のエラーが消える。
code:er.rs
errorE0277: the size for values of type [u8] cannot be known at compilation time --> jubjub/src/redjubjub.rs:295:18
|
295 | vk_2.write(&mut vk_2_bytes..).unwrap(); | ^^^^^ doesn't have a size known at compile-time
|
= help: the trait std::marker::Sized is not implemented for [u8]
errorE0277: the trait bound [u8]: std::io::Write is not satisfied --> jubjub/src/redjubjub.rs:295:18
|
295 | vk_2.write(&mut vk_2_bytes..).unwrap(); | ^^^^^ the trait std::io::Write is not implemented for [u8]
|
= help: the following implementations were found:
<&'a mut u8 as std::io::Write> = note: required because of the requirements on the impl of pairing::io::Write for [u8]
ok_or_elseはsome時に処理を実行しないのでperfomanceがok_orより良い
References
Wrapper around new futures and ThreadPool that resembles futures_cpupool